package cn.lg.soar.system.auth.service.impl;

import cn.lg.soar.common.util.current.UserContext;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.common.util.reflect.ReflectUtil;
import cn.lg.soar.common.util.reflect.SoarBeanUtils;
import cn.lg.soar.core.util.InnerSerializeUtils;
import cn.lg.soar.database.base.service.impl.BaseServiceImpl;
import cn.lg.soar.database.enums.ProvideTypeEnum;
import cn.lg.soar.database.enums.SystemValueEnum;
import cn.lg.soar.system.auth.entity.DataRuleProvide;
import cn.lg.soar.system.auth.mapper.DataRuleProvideMapper;
import cn.lg.soar.system.auth.service.IDataRuleProvideService;
import cn.lg.soar.system.auth.service.IUserService;
import cn.lg.soar.system.auth.service.UserOrgService;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.function.Supplier;

/**
 * DataRuleValue服务层实现
 * 数据源
 * @author luguoxiang
 * @since 2022-04-30
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class DataRuleProvideServiceImpl extends BaseServiceImpl<DataRuleProvideMapper, DataRuleProvide> implements IDataRuleProvideService {

    private final static Map<String, Supplier<Object>> CACHE = new ConcurrentReferenceHashMap<>();
    private final UserOrgService userOrgService;
    private final IUserService userService;

    public DataRuleProvideServiceImpl(UserOrgService userOrgService, IUserService userService) {
        this.userOrgService = userOrgService;
        this.userService = userService;
    }


    @Override
    public Object getColumnValue(ProvideTypeEnum provide, String columnValue) {
        switch (provide) {
            case INPUT: return InnerSerializeUtils.deserializeByJson(columnValue);
            case SYSTEM:
                switch (SystemValueEnum.valueOf(columnValue)) {
                    case seconds: return LocalDateTime.now().withNano(0);
                    case minutes: return LocalDateTime.now().withNano(0).withSecond(0);
                    case hours: return LocalDateTime.now().withNano(0).withSecond(0).withMinute(0);
                    case date: return LocalDate.now();
                    case month: return LocalDate.now().withDayOfMonth(1);
                    case year: return LocalDate.now().withDayOfYear(1);
                    case week: return LocalDate.now().getDayOfWeek().getValue();
                    case userId: return UserContext.getUserId();
                    case username: return UserContext.getUser().getUsername();
                    case usertype: return UserContext.getUser().getUserType();
                    case allRoleIds: return userService.getUserCurrentAllRoleIds(UserContext.getUserId());
                    case orgIds: return userOrgService.getOrgIds(UserContext.getUserId());
                    case allOrgIds: return userService.getUserCurrentAllOrgIds(UserContext.getUserId());
                    case postIds: return userOrgService.getPostIds(UserContext.getUserId());
                    default: throw new RuntimeException("未知类型");
                }
            default:
                return CACHE.computeIfAbsent(columnValue, id -> {
                    Long dataRuleProvideId = (Long) InnerSerializeUtils.deserializeByJson(id);
                    DataRuleProvide dataRuleProvide = getById(dataRuleProvideId);
                    Class<?> aClass;
                    try {
                        aClass = Class.forName(dataRuleProvide.getClassName());
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    Object bean = SoarBeanUtils.getBean(aClass);
                    String methodName = dataRuleProvide.getMethodName();
                    String params = dataRuleProvide.getParams();
                    if (DataUtil.isEmpty(params)) {
                        return () -> ReflectUtil.invoke(bean, methodName);
                    }

                    String[] split = params.split(",");
                    Object[] objects = new Object[split.length];
                    for (int i = 0; i < split.length; i++) {
                        objects[i] = InnerSerializeUtils.deserializeByJson(split[i]);
                    }
                    return () -> ReflectUtil.invoke(bean, methodName, objects);
                }).get();
        }
    }

}
